
#include <stdlib.h>

#include "serialization/asn1.h"
#include "node_ble.h"
#include "ser_dep_transport.h"

#include <HID/HidBLE.h>

#include "Debug.h"

#define BLENODE_FIRMWARE_VISIBLE_VERSION_MAJOR 0x18
#define BLENODE_FIRMWARE_VISIBLE_VERSION_MINOR 0x17
#define BLENODE_STATIC_RANDOM_ADDRESS                                                              \
    { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }
#define BLENODE_PROPERTY_VERSION_LIST                                                              \
    {                                                                                              \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  \
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00                             \
    }

int router_queue_push_inf(t1_inf_t* inf, uint16_t len);

static int node_ble_getVersion2(asn1_tlv_t cmd) {
	LOG_I("ble get version2");
    asn1_builder_t response;
    ASN1(response, ->TAG(PAYLOAD_RESPONSE)
                       ->TAG(RESPONSE_BLECOMMAND)
                       ->TAG(BLEMODULE_RESPONSE_GET_VERSION2)
                       ->TAG(BLEMODULE_RESPONSE_GET_VERSION2_VERSION)
                       ->VALUE({BLENODE_FIRMWARE_VISIBLE_VERSION_MAJOR,
                                BLENODE_FIRMWARE_VISIBLE_VERSION_MINOR})
                       ->CLOSE()
                       ->TAG(BLEMODULE_RESPONSE_GET_VERSION2_STATIC_RANDOM_ADDRESS)
                       ->VALUE(BLENODE_STATIC_RANDOM_ADDRESS)
                       ->CLOSE()
                       ->TAG(BLEMODULE_RESPONSE_GET_VERSION2_PROPERTY_VERSION_LIST)
                       ->VALUE(BLENODE_PROPERTY_VERSION_LIST)
                       ->CLOSE()
                       ->CLOSEALL());

    t1_inf_t inf = {.header = {.destination = NODE_SAM,
                               .source = NODE_BLE,
                               .reply = NODE_NULL,
                               .secure_channel = 0,
                               .RFU1 = 0,
                               .RFU2 = 0},
                    .payload = response.payload};
	router_queue_push_inf(&inf, response.dataPtr);
	return 0;
}

void app_ble_rapdu_handle(uint8_t *rapdu, uint16_t len) {
    asn1_builder_t response;

    ASN1(response, ->TAG(PAYLOAD_RESPONSE)
                       ->TAG(RESPONSE_BLECOMMAND)
                       ->TAG(BLEMODULE_RESPONSE_TRANSCEIVE)
                       ->TAG(BLEMODULE_RESPONSE_TRANSCEIVE_RAPDU)
                       ->FVALUE(rapdu, len)
                       ->CLOSEALL());

    t1_inf_t inf = {.header = {.destination = NODE_SAM,
                               .source = NODE_BLE,
                               .reply = NODE_NULL,
                               .secure_channel = 0,
                               .RFU1 = 0,
                               .RFU2 = 0},
                    .payload = response.payload};

	router_queue_push_inf(&inf, response.dataPtr);
}


static int node_ble_transceive(asn1_tlv_t cmd) {
	LOG_I("ble transceive");
	static uint8_t rapdu[512];
	uint16_t rlen = 0;
    asn1_tlv_t cApdu = asn1_unwrap(cmd.value);
    /* push to ble tx queue */
	int ret = hid_ble_tranceive(cApdu.value, cApdu.len, rapdu, &rlen, 3000);
	if(!ret){
		app_ble_rapdu_handle(rapdu, rlen);
	}
	return ret;
}

typedef enum {
    bleModuleCommandGetVersion2 = 0,
    bleModuleCommandTransceive,
    TOTAL_COMMANDS
} bleModuleCommandId_t;

typedef int (*command_handler)(asn1_tlv_t cmd);

typedef struct {
    bleModuleCommandId_t bleModuleCommandId;
    command_handler handler;
} bleModuleCommands_t;

static bleModuleCommands_t const bleModuleCommands[] = {
    [bleModuleCommandGetVersion2] = {0x0C, node_ble_getVersion2},
    [bleModuleCommandTransceive] = {0x02, node_ble_transceive},
};

int node_ble_handler(t1_inf_t *inf, /*t1_len_t*/ uint16_t len) {
    asn1_tlv_t command = asn1_unwrap(inf->payload);

    if (command.tag.tagid == bleModuleCommand) {
        asn1_tlv_t bleCommand = asn1_unwrap(command.value);
        for (bleModuleCommandId_t i = 0; i < TOTAL_COMMANDS; i++) {
            if (bleModuleCommands[i].bleModuleCommandId == bleCommand.tag.tagid) {
                return bleModuleCommands[i].handler(bleCommand);
            }
        }
        LOG_W("No handler for command %02X\n", bleCommand.tag.tagid);
    }
	return 2;
}

